2.1-this 和函数
Create by fall on — — 2020 Recently revised in 13 Oct 2023
函数
创建一个函数
function Add(a,b){
return a+b
}
const Add = (a,b)=>a+b
const Add = function(a,b){
return a+b
}
传入值的解构
// 可以通过解构获取对象上对应的属性进行读取
function dog({name,sex,master}){
console.log(`这只狗是${master}先生家的一条狗,是一条${sex}${name}`);
}
// 传入值的时候,可以通过指定的值进行传递,不必按顺序
doubleH({
name:"2哈",
master:"刘",
sex:'male',
})
### 箭头函数
箭头函数细节注意
- 箭头函数不能用 new,不能作为构造函数
- 箭头函数如果返回一个对象,一定要用
()
把对象包住,否则会识别错误 - 箭头函数中的 this 指向上一层函数的主人
普通函数转化为箭头函数
// 普通函数声明
function example(x){
return x+10;
}
// 最简单的箭头函数声明
var example = x => x+10
// 当返回值是对象的时候,一定要加小括号
// 没有参数的时候,使用小括号代替,多个参数时,也需要用小括号进行包裹
var example = () =>({
username:'kill',
tapL:12;
})
//无返回值函数声明
function add(){
alert('hello world');
}
var show = () =>{
alert('hello world')
}
以下情况可以使用箭头函数,以简化操作。
var arr = [10,22,33,44,55];
//原函数
var filterArr = arr.filter(function(item){
return item<20;
})
filterArr // [10]
//使用箭头函数
var newArr = arr.filter(item => item<20);
newArr // [10]
注释
好的注释一般有以下特点
-
描述架构:对组件进行高层次的整体概括,它们如何相互作用、各种情况下的控制流程是什么样的……简而言之 —— 代码的鸟瞰图。有一个专门用于构建代码的高层次架构图 UML 值得学习。
-
记录函数的参数和用法:有一个专门用于记录函数的语法 JSDoc:用法、参数和返回
可能遇到这种情况,打开了很久之前写过的代码,然后觉得这代码写的一点也不符合“第一直觉”,想重写,然后兴冲冲地重写一遍。写完了,运行的时候才想起来为什么写的不符合第一直觉了,但是时间已经浪费。
解决方案的注释非常的重要,可以帮助你以正确的方式继续开发。
自描述型注释
通过英文的方法名,作为注释,称为自描述型注释
function isPrime(n) {
for (let i = 2; i < n; i++) {
if (n % i == 0) return false;
}
return true;
}
解释性注释通常来说都是不好的,都可以使用自描述性注释代替。
JSDoc
VS Code 可以采用 JSDoc 对 JS 的方法进行注释
注释中的类型可采用 TypeScript 中的类型,有关 TypeScript 内容可以查看 TypeScript 笔记
注释可以通过工具直接生成文档。
/**
* 从对象中移除属性
* @param {object} propArray 用于生成上下文
* @param {Array<string>} except 属性数组
* @return {boolean} 是否清除成功
*/
function removeProp(obj,except) {
if(Array.isArray(except)){
except.forEach(item){
delete obj[item]
}
return true
}
return false
}
@author
该类/方法的作者。@class
表示这是一个类。@function/@method
表示这是一个函数/方法(这是同义词)。@private
表示该类/方法是私有的,JSDOC 不会为其生成文档。@name
该类/方法的名字。@description
该类/方法的描述。@param
该类/方法的参数,可重复定义。@return
该类/方法的返回类型。@link
创建超链接,生成文档时可以为其链接到其他部分。@example
创建例子。
构造函数的封装
可以查看 2.2 章节 面向对象编程
函数的 name
// 所有的函数上面都有 name 属性
// 函数的名称
let person = {
sayHello(){}
}
person.sayHello.name // sayHello
// 没有赋值,或者定义
(()=>{}).name // ''
// 有赋值
const foo = function(){}
// new 创建
(new Function).name // anonymous
// 绑定 bind 后生成的函数
foo.bind({}).name // bound foo
构造函数
// 类声明
function Dog(name){
this.name = name
this.voice = ()=>{
console.log('汪汪汪')
}
}
// 类表达式:可以命名(类的 name 属性取类名),也可以不命名(类的 name 属性取变量名)
let dog = new Dog('xueli')
函数的应用
闭包
- 函数嵌套函数
- 内部函数使用外部函数的形参和变量(避免污染全局变量)
- 被引用的变量不会被垃圾回收机制回收(常驻内存)
使用闭包时需要注意内存泄漏:如果存储了很大的对象,且没有手动释放内存,无法释放自动内存
JavaScript 引擎如何处理相同的变量
- 使用一个变量,JavaScript 引擎会在当前的执行上下文中查找变量,如果没有找到,会继续在 outer(执行环境指向外部执行上下文的引用)所指向的执行上下文中查找;
- JavaScript 执行过程,作用域链是由词法作用域决定,而词法作用域是由代码中函数声明的位置决定;
- 根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使外部函数已经执行结束了,但是内部函数引用外部函数的变量依旧保存在内存中,把这些变量的集合称为闭包
立即执行函数
// 立即执行函数
(function(){
console.log('老子爱你,么么哒')
})()
((word)=>{
console.log(word)
})('来呀,快活啊')
解决内存泄漏
var click = function(){
var oDiv = document.getElementById('oDiv')
var oDivId = oDiv
return function(){
console.log(oDiv)
console.log(oDivId)
}
oDiv = null
}
回调函数
回调函数意思是,在函数执行完成之后,return 时执行一个函数,可以实现线性执行函数。
// 回调函数
var chain = function(a,b){
return double(a+b)
}
function double(a){
return a*a
}
chain(5,5) // 100
函数递归
如果函数是自己调用自己,就是递归函数。
递归实现 1+2+3+...+100
function add(num,all,another){
if(num==1){
return all
}
return add(num-1, (num-1)*num)
}
// n 个数的阶乘
function foo(num){
if(num===1){
return 1
}
return num*foo(num-1)
}
foo(10)
// 楼梯问题,总共有 n 阶楼梯,可以上 1 节,可以上 2 节,用工有多少种上楼梯的方法
function foo(x){
if(x==1){
return 1
}else if(x==2){
return 2
}else if(x==3){
return 3
}
return foo(x-2)+foo(x-1)
}
// 斐波那契数列
function foo(x){
if(x==1)return 1
if(x==2)return 1
return foo(x-1)+foo(x-2)
}
// 算法优化
function fuu(x,y=1,z=1){
if(x==1)return y
return fuu(x-1,z,y+z)
}
// 常规递归的斐波那契函数
function Fibonacci(n) {
if ( n <= 1 ) {return 1}
return Fibonacci(n - 1) + Fibonacci(n - 2)
}
Fibonacci(100) // 超时
// 尾递归优化后的斐波那契函数
function fib(n,fi1,fi2){
let fib1 =fi1||1
let fib2 =fi2||1
if(n<=1){
return fi1
}
let next = fib1 +fib2
return fib(n-1,fib2,next)
}
// n = 5 时,
// 4 1 2
// 3 2 3
// 2 3 5
// 1 5
// 1 1 2 3 5
Fibonacci2(100) // 573147844013817200000
高阶函数
高阶函数是可以通过
function addWith(num1){
return function(num2){
return num1 + num2
}
}
const addSeven = addWith(7)
addSeven(22)
addSeven(21)
// 立即执行函数和高阶函数结合
var a = (function(){
var a =1
retrun function(){
console.log(a++)
}
})()
this
除箭头函数外,任何一个函数都会内置名为 this 的变量,this 变量存储的是当前函数主人的地址。
箭头函数没有自己的指向,所以箭头函数内的 this 就是外面一层的 this
<script>
// onClick 方法的主人是 button 对象,所以,this指向为 <button> 标签
window.onload =function(){
var aBtn = document.getElementsByTagName("button");
for(var i = 0 ;i<aBtn.length;i++){
aBtn[i].onclick = function(){
console.log(this);
}
}
}
</script>
<body>
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
</body>
常见三种指向
// 1.全局空间命名的函数,指向 window
function show(){
console.log(this);// 指向 window
function showChild(){
console.log(this) // 当前函数主人的地址,也是 window
}
}
// 2.向当前方法名或对象名,this->person
var person = {
username : 'iron-man',
show:function(){
console.log(this.username);
}
}
console.log(persion.username)
// 3.this 指向当前事件的触发者
window.onload = function(){
var oBtn = document.getElementById('btn');
oBtn.onclick = function(){
console.log(this.innerHTML);
}
}
// 注:在箭头函数中,this指向当前函数的父元素
// 构造函数中 this 的指向
function Cool(){
this.name = 'fake'
this.show =()=>{
console.log(this);
}
this.show() // 指向 window
}
const cool = new Cool()
cool.show() // 指向 cool
// 匿名函数的执行具有全局性,则匿名函数中的 this 指向是 window,而不是调用该匿名函数的对象;
let obj = {
bob:12,
getThis: function () {
console.log(this.bob);
return function add() {
console.log(this);
}
}
}
obj.getThis()(); //window
改变指向
箭头函数中的 this 是在函数定义的时候就确定下来的,而不是在函数调用的时候确定的,并且箭头函数没有 apply
、call
、bind
方法,无法改变指向。
强制改变 this 指向
call
- 第一个参数为 this 指向的值,传入什么,指向什么
- 从第二个参数开始:将原函数的参数往后顺延一位
apply
- 第一个参数传入参数为 this 指向的值,传入什么,指向什么
- 第二个参数:用数组,放入我们原有的所有参数
bind 预设 this 指向
- 使用
var fun = foo.bind()
,使用方法和 call & apply 不同 - 返回一个改变
this
指向的函数。- 函数功能不变,this 指向传入的第一个参数。
// call的使用
var arr = [50,44,22,64];
Math.min.apply(null,arr)
const {toString:getString} = []
const foo = []
foo.getString = getString
getString.apply([20,12,66])
//bind 的使用方法
function take(name,type){
console.log(name+"云云云"+type+this);
};
var aftertake = take.bind("bind")(11,"make");
Tips: call 和 apply 是改变 this 指向后立即调用函数,bind 是返回一个改变 this 指向的函数
参考文章
作者 | 文章名 |
---|---|
Mondo | JS 工具库文档化 - JSDoc |
现代 JavaScript 教程 | 注释 |
前端南玖 | 2022年了你还不了解箭头函数与普通函数的区别吗? |
牛客网 | 前端工程师面试宝典 |